{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 让多个智能体在多智能体层级中协作 🤖🤝🤖  \n",
    "_作者：[Aymeric Roucher](https://huggingface.co/m-ric)_\n",
    "\n",
    "> 本教程属于进阶内容，建议先了解[另一本指南](agents)中的概念！\n",
    "\n",
    "在本笔记本中，我们将构建一个**多智能体网页浏览器：一个多个智能体协作，通过互联网解决问题的智能体系统！**\n",
    "\n",
    "它将是一个简单的层级结构，使用 `ManagedAgent` 对象来封装受管理的网页搜索智能体：\n",
    "\n",
    "```\n",
    "              +----------------+\n",
    "              | 管理员智能体    |\n",
    "              +----------------+\n",
    "                       |\n",
    "        _______________|______________\n",
    "       |                              |\n",
    "  代码解释器       +--------------------------------+\n",
    "       工具         |         受管理的智能体        |\n",
    "                     |      +------------------+      |\n",
    "                     |      | 网页搜索智能体   |      |\n",
    "                     |      +------------------+      |\n",
    "                     |         |            |         |\n",
    "                     |  网页搜索工具       |         |\n",
    "                     |             访问网页工具   |\n",
    "                     +--------------------------------+\n",
    "```\n",
    "\n",
    "让我们开始搭建这个系统。\n",
    "\n",
    "⚡️ 我们的智能体将由 [meta-llama/Meta-Llama-3.1-70B-Instruct](https://huggingface.co/meta-llama/Meta-Llama-3.1-70B-Instruct) 提供支持，使用 `HfApiEngine` 类，它利用了 HF 的推理 API：推理 API 可以快速、轻松地运行任何操作系统模型。\n",
    "\n",
    "运行以下命令来安装所需的依赖项："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install markdownify duckduckgo-search \"transformers[agents]\" --upgrade -q"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们将选择使用由 [Qwen/Qwen2.5-72B-Instruct](https://huggingface.co/Qwen/Qwen2.5-72B-Instruct) 提供支持的模型，因为它非常强大，并且在 HF API 中可以免费使用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = \"Qwen/Qwen2.5-72B-Instruct\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 🔍 创建一个网页搜索工具\n",
    "\n",
    "对于网页浏览，我们可以直接使用我们现有的 [`DuckDuckGoSearchTool`](https://github.com/huggingface/transformers/blob/main/src/transformers/agents/search.py) 工具来提供一个类似于 Google 搜索的功能。\n",
    "\n",
    "但是，我们还需要能够查看 `DuckDuckGoSearchTool` 找到的网页内容。  \n",
    "为此，我们可以导入库中内建的 `VisitWebpageTool`，但我们将重新构建这个工具，以便了解它是如何实现的。\n",
    "\n",
    "因此，让我们从零开始，使用 `markdownify` 创建我们的 `VisitWebpageTool` 工具。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "import re\n",
    "import requests\n",
    "from markdownify import markdownify as md\n",
    "from requests.exceptions import RequestException\n",
    "from transformers.agents import tool\n",
    "\n",
    "\n",
    "@tool\n",
    "def visit_webpage(url: str) -> str:\n",
    "    \"\"\"Visits a webpage at the given URL and returns its content as a markdown string.\n",
    "\n",
    "    Args:\n",
    "        url: The URL of the webpage to visit.\n",
    "\n",
    "    Returns:\n",
    "        The content of the webpage converted to Markdown, or an error message if the request fails.\n",
    "    \"\"\"\n",
    "    try:\n",
    "        # Send a GET request to the URL\n",
    "        response = requests.get(url)\n",
    "        response.raise_for_status()  # Raise an exception for bad status codes\n",
    "\n",
    "        # Convert the HTML content to Markdown\n",
    "        markdown_content = md(response.text).strip()\n",
    "\n",
    "        # Remove multiple line breaks\n",
    "        markdown_content = re.sub(r\"\\n{3,}\", \"\\n\\n\", markdown_content)\n",
    "\n",
    "        return markdown_content\n",
    "\n",
    "    except RequestException as e:\n",
    "        return f\"Error fetching the webpage: {str(e)}\"\n",
    "    except Exception as e:\n",
    "        return f\"An unexpected error occurred: {str(e)}\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "好了，现在让我们初始化并测试我们的工具！"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hugging Face \\- Wikipedia\n",
      "\n",
      "[Jump to content](#bodyContent)\n",
      "\n",
      "Main menu\n",
      "\n",
      "Main menu\n",
      "move to sidebar\n",
      "hide\n",
      "\n",
      " Navigation\n",
      " \n",
      "\n",
      "* [Main page](/wiki/Main_Page \"Visit the main page [z]\")\n",
      "* [Contents](/wiki/Wikipedia:Contents \"Guides to browsing Wikipedia\")\n",
      "* [Current events](/wiki/Portal:Current_events \"Articles related to current events\")\n",
      "* [Random article](/wiki/Special:Random \"Visit a randomly selected article [x]\")\n",
      "* [About Wikipedia](/wiki/Wikipedia:About \"Learn about Wikipedia and how it works\")\n",
      "* [Co\n"
     ]
    }
   ],
   "source": [
    "print(visit_webpage(\"https://en.wikipedia.org/wiki/Hugging_Face\")[:500])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 构建我们的多智能体系统 🤖🤝🤖\n",
    "\n",
    "现在我们已经拥有了所有的工具 `search` 和 `visit_webpage`，可以使用它们来创建网页智能体。\n",
    "\n",
    "该选择什么配置呢？  \n",
    "- 网页浏览是一个单线程任务，不需要并行调用工具，因此使用 JSON 调用工具非常合适。因此，我们选择 `ReactJsonAgent`。  \n",
    "- 此外，由于有时网页搜索需要浏览多个页面才能找到正确答案，我们更倾向于将 `max_iterations` 增加到 10。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers.agents import (\n",
    "    ReactCodeAgent,\n",
    "    ReactJsonAgent,\n",
    "    HfApiEngine,\n",
    "    ManagedAgent,\n",
    ")\n",
    "from transformers.agents.search import DuckDuckGoSearchTool\n",
    "\n",
    "llm_engine = HfApiEngine(model)\n",
    "\n",
    "web_agent = ReactJsonAgent(\n",
    "    tools=[DuckDuckGoSearchTool(), visit_webpage],\n",
    "    llm_engine=llm_engine,\n",
    "    max_iterations=10,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "然后，我们将这个智能体封装到一个 `ManagedAgent` 中，这样它就可以通过管理员智能体进行调用了。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "managed_web_agent = ManagedAgent(\n",
    "    agent=web_agent,\n",
    "    name=\"search\",\n",
    "    description=\"Runs web searches for you. Give it your query as an argument.\",\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "最后，我们创建一个管理员智能体，并在初始化时将我们的受管理智能体通过 `managed_agents` 参数传递给它。\n",
    "\n",
    "由于这个智能体负责规划和思考，因此高级推理将非常有帮助，所以选择 `ReactCodeAgent` 是最合适的。\n",
    "\n",
    "另外，我们想提一个涉及当前年份的问题：因此，我们需要添加 `additional_authorized_imports=[\"time\", \"datetime\"]`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "manager_agent = ReactCodeAgent(\n",
    "    tools=[],\n",
    "    llm_engine=llm_engine,\n",
    "    managed_agents=[managed_web_agent],\n",
    "    additional_authorized_imports=[\"time\", \"datetime\"],\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "就这样！现在让我们运行我们的系统！我们选择一个需要进行一些计算的问题"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\u001b[32;20;1m======== New task ========\u001b[0m\n",
      "\u001b[37;1mHow many years ago was Stripe founded?\u001b[0m\n",
      "\u001b[33;1m=== Agent thoughts:\u001b[0m\n",
      "\u001b[0mThought: I need to find out when Stripe was founded and then calculate the number of years since then. I will start by using the `search` tool to find the founding year of Stripe.\u001b[0m\n",
      "\u001b[33;1m>>> Agent is executing the code below:\u001b[0m\n",
      "\u001b[0m\u001b[38;5;7mfounding_year\u001b[39m\u001b[38;5;7m \u001b[39m\u001b[38;5;109;01m=\u001b[39;00m\u001b[38;5;7m \u001b[39m\u001b[38;5;7msearch\u001b[39m\u001b[38;5;7m(\u001b[39m\u001b[38;5;144m\"\u001b[39m\u001b[38;5;144mWhen was Stripe founded\u001b[39m\u001b[38;5;144m\"\u001b[39m\u001b[38;5;7m)\u001b[39m\n",
      "\u001b[38;5;109mprint\u001b[39m\u001b[38;5;7m(\u001b[39m\u001b[38;5;144m\"\u001b[39m\u001b[38;5;144mFounding year:\u001b[39m\u001b[38;5;144m\"\u001b[39m\u001b[38;5;7m,\u001b[39m\u001b[38;5;7m \u001b[39m\u001b[38;5;7mfounding_year\u001b[39m\u001b[38;5;7m)\u001b[39m\u001b[0m\n",
      "\u001b[33;1m====\u001b[0m\n",
      "\u001b[32;20;1m======== New task ========\u001b[0m\n",
      "\u001b[37;1mYou're a helpful agent named 'search'.\n",
      "You have been submitted this task by your manager.\n",
      "---\n",
      "Task:\n",
      "When was Stripe founded\n",
      "---\n",
      "You're helping your manager solve a wider task: so make sure to not provide a one-line answer, but give as much information as possible so that they have a clear understanding of the answer.\n",
      "\n",
      "Your final_answer WILL HAVE to contain these parts:\n",
      "### 1. Task outcome (short version):\n",
      "### 2. Task outcome (extremely detailed version):\n",
      "### 3. Additional context (if relevant):\n",
      "\n",
      "Put all these in your final_answer tool, everything that you do not pass as an argument to final_answer will be lost.\n",
      "And even if your task resolution is not successful, please return as much context as possible, so that your manager can act upon this feedback.\u001b[0m\n",
      "\u001b[33;1m=== Agent thoughts:\u001b[0m\n",
      "\u001b[0mThought: I need to find the founding year of Stripe and related details. The best way to start is by performing a web search.\u001b[0m\n",
      "\u001b[33;1m>>> Calling tool: 'web_search' with arguments: {'query': 'When was Stripe founded'}\u001b[0m\n",
      "\u001b[33;1m=== Agent thoughts:\u001b[0m\n",
      "\u001b[0mThought: The search results provide information on when Stripe was founded and additional details about the company. I will now visit the Stripe Wikipedia page for a more detailed overview.\u001b[0m\n",
      "\u001b[33;1m>>> Calling tool: 'visit_webpage' with arguments: {'url': 'https://en.wikipedia.org/wiki/Stripe,_Inc.'}\u001b[0m\n",
      "\u001b[33;1m=== Agent thoughts:\u001b[0m\n",
      "\u001b[0mThought: I have collected detailed information about Stripe from the Wikipedia page. Now, I will formulate a comprehensive final answer as required for the task.\u001b[0m\n",
      "\u001b[33;1m>>> Calling tool: 'final_answer' with arguments: {'answer': \"### 1. Task Outcome (short version):\\nStripe, Inc. was founded in 2010 by Irish brothers John Collison and Patrick Collison, who serve as the company's president and CEO, respectively. The company is headquartered in South San Francisco, California, and Dublin, Ireland. Stripe provides payment processing and financial services for businesses, enabling them to accept payments and manage financial transactions online.\\n\\n### 2. Task Outcome (extremely detailed version):\\nStripe, Inc. is an Irish-American multinational financial services and software as a service (SaaS) company co-founded in 2010 by John Collison and Patrick Collison, two Irish brothers. The company is dual-headquartered in South San Francisco, California, and Dublin, Ireland. Stripe offers a wide range of financial services and tools, primarily focused on payment processing and management for businesses. Some key milestones and details include:\\n\\n- **Founding and Early Years:** The company was founded in 2010 in Palo Alto, California. In 2011, it received a $2 million investment from notable figures such as Elon Musk, Peter Thiel, and venture capital firms like Sequoia Capital. In 2012, Stripe launched its first multiparty payments solution, Stripe Connect.\\n- **Growth and Expansion:** Stripe has rapidly expanded its services and reach. In 2013, it made its first acquisition, Kickoff, a chat and task management application. In 2016, Stripe launched Atlas, a platform to help startups register as U.S. corporations. The company has continued to grow, raising significant rounds of funding and expanding its services to new markets, including Europe and Africa.\\n- **Product Suite:** Stripe offers a comprehensive suite of financial tools, including payment processing, billing, fraud prevention, point-of-sale solutions, and more. Notable products include Radar (anti-fraud tools), Terminal (point-of-sale hardware), and Stripe Capital (merchant cash advances).\\n- **Partnerships and Integrations:** Stripe has formed partnerships with major companies such as Ford, Spotify, and Twitter to handle transactions and payments. It has also launched the Stripe App Marketplace, allowing businesses to integrate third-party apps and services.\\n- **Valuation and Funding:** As of the latest data, Stripe has raised over $6.5 billion in funding and is valued at around $70 billion, making it one of the most valuable privately-held startups globally.\\n- **Challenges and Layoffs:** In 2022, Stripe announced layoffs, cutting 14% of its workforce to prepare for leaner times. However, the company continues to innovate and expand its offerings.\\n\\n### 3. Additional Context (if relevant):\\n- **Impact on the Founders:** John and Patrick Collison have been influential in shaping the fintech industry. Their vision and leadership have driven Stripe's success and innovation.\\n- **Industry Position:** Stripe is a leader in the fintech sector, competing with other payment processors and financial service providers. Its robust product suite and global reach have solidified its position in the market.\\n- **Future Outlook:** Stripe continues to invest in new technologies and services, including AI and carbon capture initiatives. The company's focus on innovation and customer needs positions it well for future growth.\"}\u001b[0m\n",
      "\u001b[33;1mPrint outputs:\u001b[0m\n",
      "\u001b[32;20mFounding year: ### 1. Task Outcome (short version):\n",
      "Stripe, Inc. was founded in 2010 by Irish brothers John Collison and Patrick Collison, who serve as the company's president and CEO, respectively. The company is headquartered in South San Francisco, California, and Dublin, Ireland. Stripe provides payment processing and financial services for businesses, enabling them to accept payments and manage financial transactions online.\n",
      "\n",
      "### 2. Task Outcome (extremely detailed version):\n",
      "Stripe, Inc. is an Irish-American multinational financial services and software as a service (SaaS) company co-founded in 2010 by John Collison and Patrick Collison, two Irish brothers. The company is dual-headquartered in South San Francisco, California, and Dublin, Ireland. Stripe offers a wide range of financial services and tools, primarily focused on payment processing and management for businesses. Some key milestones and details include:\n",
      "\n",
      "- **Founding and Early Years:** The company was founded in 2010 in Palo Alto, California. In 2011, it received a $2 million investment from notable figures such as Elon Musk, Peter Thiel, and venture capital firms like Sequoia Capital. In 2012, Stripe launched its first multiparty payments solution, Stripe Connect.\n",
      "- **Growth and Expansion:** Stripe has rapidly expanded its services and reach. In 2013, it made its first acquisition, Kickoff, a chat and task management application. In 2016, Stripe launched Atlas, a platform to help startups register as U.S. corporations. The company has continued to grow, raising significant rounds of funding and expanding its services to new markets, including Europe and Africa.\n",
      "- **Product Suite:** Stripe offers a comprehensive suite of financial tools, including payment processing, billing, fraud prevention, point-of-sale solutions, and more. Notable products include Radar (anti-fraud tools), Terminal (point-of-sale hardware), and Stripe Capital (merchant cash advances).\n",
      "- **Partnerships and Integrations:** Stripe has formed partnerships with major companies such as Ford, Spotify, and Twitter to handle transactions and payments. It has also launched the Stripe App Marketplace, allowing businesses to integrate third-party apps and services.\n",
      "- **Valuation and Funding:** As of the latest data, Stripe has raised over $6.5 billion in funding and is valued at around $70 billion, making it one of the most valuable privately-held startups globally.\n",
      "- **Challenges and Layoffs:** In 2022, Stripe announced layoffs, cutting 14% of its workforce to prepare for leaner times. However, the company continues to innovate and expand its offerings.\n",
      "\n",
      "### 3. Additional Context (if relevant):\n",
      "- **Impact on the Founders:** John and Patrick Collison have been influential in shaping the fintech industry. Their vision and leadership have driven Stripe's success and innovation.\n",
      "- **Industry Position:** Stripe is a leader in the fintech sector, competing with other payment processors and financial service providers. Its robust product suite and global reach have solidified its position in the market.\n",
      "- **Future Outlook:** Stripe continues to invest in new technologies and services, including AI and carbon capture initiatives. The company's focus on innovation and customer needs positions it well for future growth.\n",
      "\u001b[0m\n",
      "\u001b[33;1m=== Agent thoughts:\u001b[0m\n",
      "\u001b[0mThought: The search result shows that Stripe was founded in 2010. Now I need to calculate how many years ago that was. I will use the current year to make this calculation.\u001b[0m\n",
      "\u001b[33;1m>>> Agent is executing the code below:\u001b[0m\n",
      "\u001b[0m\u001b[38;5;109;01mimport\u001b[39;00m\u001b[38;5;7m \u001b[39m\u001b[38;5;109mdatetime\u001b[39m\n",
      "\n",
      "\u001b[38;5;7mcurrent_year\u001b[39m\u001b[38;5;7m \u001b[39m\u001b[38;5;109;01m=\u001b[39;00m\u001b[38;5;7m \u001b[39m\u001b[38;5;7mdatetime\u001b[39m\u001b[38;5;109;01m.\u001b[39;00m\u001b[38;5;7mdatetime\u001b[39m\u001b[38;5;109;01m.\u001b[39;00m\u001b[38;5;7mnow\u001b[39m\u001b[38;5;7m(\u001b[39m\u001b[38;5;7m)\u001b[39m\u001b[38;5;109;01m.\u001b[39;00m\u001b[38;5;7myear\u001b[39m\n",
      "\u001b[38;5;7mfounding_year\u001b[39m\u001b[38;5;7m \u001b[39m\u001b[38;5;109;01m=\u001b[39;00m\u001b[38;5;7m \u001b[39m\u001b[38;5;139m2010\u001b[39m\n",
      "\u001b[38;5;7myears_since_founded\u001b[39m\u001b[38;5;7m \u001b[39m\u001b[38;5;109;01m=\u001b[39;00m\u001b[38;5;7m \u001b[39m\u001b[38;5;7mcurrent_year\u001b[39m\u001b[38;5;7m \u001b[39m\u001b[38;5;109;01m-\u001b[39;00m\u001b[38;5;7m \u001b[39m\u001b[38;5;7mfounding_year\u001b[39m\n",
      "\u001b[38;5;7mfinal_answer\u001b[39m\u001b[38;5;7m(\u001b[39m\u001b[38;5;7myears_since_founded\u001b[39m\u001b[38;5;7m)\u001b[39m\u001b[0m\n",
      "\u001b[33;1m====\u001b[0m\n",
      "\u001b[33;1mPrint outputs:\u001b[0m\n",
      "\u001b[32;20m\u001b[0m\n",
      "\u001b[33;1mLast output from code snippet:\u001b[0m\n",
      "\u001b[32;20m14\u001b[0m\n",
      "\u001b[32;20;1mFinal answer:\u001b[0m\n",
      "\u001b[32;20m14\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "14"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "manager_agent.run(\"How many years ago was Stripe founded?\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们的智能体成功地高效协作，解决了任务！ ✅\n",
    "\n",
    "💡 你可以轻松地扩展到更多智能体：一个负责代码执行，一个负责网页搜索，一个负责文件加载……\n",
    "\n",
    "🤔💭 甚至可以考虑构建更复杂的树状层级结构，一个 CEO 智能体负责管理多个中层经理，每个经理下有多个报告。\n",
    "\n",
    "我们甚至可以添加更多的中间管理层，每层都有多个日常会议，进行大量敏捷工作和 Scrum 主管，每个新增的组件都会增加足够的摩擦力，确保任务永远无法完成……嗯，等等，不，我们还是坚持我们的简单结构吧。\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "cookbook2",
   "language": "python",
   "name": "cookbook2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
